use std::{path::Path, process::Command, time::Duration};

use anyhow::Result;
use rand::{Rng, distributions::Alphanumeric};
use serde::Deserialize;
use tauri::{AppHandle, Manager, State};
use tauri_plugin_opener::OpenerExt;
use tracing::info;

use crate::{
    DatabaseManager,
    api::window_api,
    constants::RESOURCE_LAUNCH_INTERVAL,
    db::{
        launcher,
        launcher_resource::{self, CreateResourceParam, LauncherResource},
        settings,
    },
    error::OneClickLaunchError,
    events::{
        EventDispatcher,
        types::{
            LauncherBasicInfoUpdated, LauncherBasicInfoUpdatedPayload, LauncherLaunched,
            LauncherLaunchedPayload,
        },
    },
};

/// 创建新的启动器
#[tauri::command]
pub async fn craete_launcher(
    app: AppHandle,
    db: State<'_, DatabaseManager>,
    name: Option<String>,
) -> Result<i64, OneClickLaunchError> {
    let name = name
        .filter(|s| !s.is_empty())
        .unwrap_or_else(generate_default_launcher_name);
    let launcher_id = launcher::create(&db.pool, &name, None).await?;

    let _ = EventDispatcher::<LauncherBasicInfoUpdated>::send_event(
        &app,
        LauncherBasicInfoUpdatedPayload {
            launcher_ids: vec![launcher_id],
        },
    );

    Ok(launcher_id)
}

fn generate_default_launcher_name() -> String {
    let mut name = "双击编辑".to_string();
    let rad_str = generate_random_string(4);
    name.push_str(&rad_str);
    name
}

fn generate_random_string(length: usize) -> String {
    rand::thread_rng()
        .sample_iter(&Alphanumeric)
        .take(length)
        .map(char::from)
        .collect()
}

/// 修改启动器名称
#[tauri::command]
pub async fn modify_launcher_name(
    app: AppHandle,
    db: State<'_, DatabaseManager>,
    launcher_id: i64,
    name: String,
) -> Result<(), OneClickLaunchError> {
    launcher::modify_launcher_name(&db.pool, launcher_id, &name).await?;

    let _ = EventDispatcher::<LauncherBasicInfoUpdated>::send_event(
        &app,
        LauncherBasicInfoUpdatedPayload {
            launcher_ids: vec![launcher_id],
        },
    );

    Ok(())
}

/// 复制启动器,包含启动器关联的资源数据
#[tauri::command]
pub async fn copy_launcher(
    app: AppHandle,
    db: State<'_, DatabaseManager>,
    launcher_id: i64,
) -> Result<i64, OneClickLaunchError> {
    let mut tx = db.pool.begin().await?;

    // 1. 复制启动器
    let launcher = launcher::find_by_id(&mut tx, launcher_id).await?;

    let launcher_resoures = launcher_resource::query_by_launcher_id(&mut tx, launcher_id).await?;

    let new_name = format!("{}-副本", launcher.name);

    // 2. 复制资源
    let new_launcher_id = launcher::create(&mut tx, &new_name, Some(launcher.sort)).await?;

    for res in launcher_resoures.iter() {
        launcher_resource::create(&mut tx, new_launcher_id, &res.name, &res.path).await?;
    }

    tx.commit().await?;

    let _ = EventDispatcher::<LauncherBasicInfoUpdated>::send_event(
        &app,
        LauncherBasicInfoUpdatedPayload {
            launcher_ids: vec![launcher_id],
        },
    );

    Ok(new_launcher_id)
}

/// 查询启动器列表
#[tauri::command]
pub async fn query_launchers(
    db: State<'_, DatabaseManager>,
) -> Result<Vec<LauncherVo>, OneClickLaunchError> {
    let launchers = launcher::query(&db.pool).await?;

    let resources = launcher_resource::query_all(&db.pool).await?;

    let launcher_vos = launchers
        .into_iter()
        .map(|launcher| {
            let res_vos = resources
                .iter()
                .filter(|resource| resource.launcher_id == launcher.id)
                .map(|resource| LauncherResourceVo {
                    id: resource.id,
                    launcher_id: resource.launcher_id,
                    name: resource.name.clone(),
                    path: resource.path.clone(),
                })
                .collect();
            LauncherVo {
                id: launcher.id,
                name: launcher.name,
                resources: res_vos,
            }
        })
        .collect();

    Ok(launcher_vos)
}

#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct LauncherVo {
    pub id: i64,
    pub name: String,
    pub resources: Vec<LauncherResourceVo>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct LauncherResourceVo {
    pub id: i64,
    pub launcher_id: i64,
    pub name: String,
    pub path: String,
}

/// 删除启动器
#[tauri::command]
pub async fn delete_launcher(
    app: AppHandle,
    db: State<'_, DatabaseManager>,
    launcher_id: i64,
) -> Result<(), OneClickLaunchError> {
    let mut tx = db.pool.begin().await?;

    launcher::delete_by_id(&mut tx, launcher_id).await?;

    launcher_resource::delete_by_launcher(&mut tx, launcher_id).await?;

    tx.commit().await?;

    let _ = EventDispatcher::<LauncherBasicInfoUpdated>::send_event(
        &app,
        LauncherBasicInfoUpdatedPayload {
            launcher_ids: vec![launcher_id],
        },
    );

    Ok(())
}

#[derive(serde::Deserialize, serde::Serialize)]
pub struct LauncherSort {
    id: i64,
    sort: i32,
}

/// 调整启动器顺序
#[tauri::command]
pub async fn modify_launcher_sort(
    app: AppHandle,
    db: State<'_, DatabaseManager>,
    launchers: Vec<LauncherSort>,
) -> Result<(), OneClickLaunchError> {
    let mut tx = db.pool.begin().await?;

    for ls in launchers.iter() {
        launcher::modify_launcher_sort(&mut tx, ls.id, ls.sort).await?
    }

    tx.commit().await?;

    let _ = EventDispatcher::<LauncherBasicInfoUpdated>::send_event(
        &app,
        LauncherBasicInfoUpdatedPayload {
            launcher_ids: launchers.iter().map(|e| e.id).collect(),
        },
    );

    Ok(())
}

/// 为启动器添加资源
#[tauri::command]
pub async fn add_resource(
    db: State<'_, DatabaseManager>,
    launcher_id: i64,
    name: Option<String>,
    path: &str,
) -> Result<i64, OneClickLaunchError> {
    let name = name.unwrap_or_else(|| generate_name(path));

    let resource_id = launcher_resource::create(&db.pool, launcher_id, &name, path).await?;

    Ok(resource_id)
}

/// 为启动器添加资源
#[tauri::command]
pub async fn add_resources(
    db: State<'_, DatabaseManager>,
    launcher_id: i64,
    resources: Vec<ResourceParam>,
) -> Result<(), OneClickLaunchError> {
    let crps = resources
        .into_iter()
        .map(|r| CreateResourceParam {
            name: r
                .name
                .filter(|s| !s.is_empty())
                .unwrap_or_else(|| generate_name(r.path.as_str())),
            path: r.path,
        })
        .collect::<Vec<CreateResourceParam>>();

    launcher_resource::create_resources(&db.pool, launcher_id, &crps).await?;

    Ok(())
}

#[derive(Deserialize, Debug)]
pub struct ResourceParam {
    pub name: Option<String>,
    pub path: String,
}

fn generate_name(path: &str) -> String {
    if path.starts_with("http") {
        return path.to_string();
    }

    match path.rfind('\\') {
        Some(index) => path[index + 1..].to_string(),
        None => path.to_string(),
    }
}

/// 修改资源名称
#[tauri::command]
pub async fn modify_resource_name(
    db: State<'_, DatabaseManager>,
    resource_id: i64,
    name: &str,
) -> Result<(), OneClickLaunchError> {
    launcher_resource::modify_name(&db.pool, resource_id, name).await?;
    Ok(())
}

/// 修改资源路径
#[tauri::command]
pub async fn modify_resource_path(
    db: State<'_, DatabaseManager>,
    resource_id: i64,
    path: &str,
) -> Result<(), OneClickLaunchError> {
    launcher_resource::modify_path(&db.pool, resource_id, path).await?;
    Ok(())
}

/// 删除启动器中的资源
#[tauri::command]
pub async fn delete_resource(
    db: State<'_, DatabaseManager>,
    resource_id: i64,
) -> Result<(), OneClickLaunchError> {
    launcher_resource::delete_by_id(&db.pool, resource_id).await?;
    Ok(())
}

/// 启动启动器
#[tauri::command]
pub async fn launch(app: AppHandle, launcher_id: i64) -> Result<(), OneClickLaunchError> {
    let db: State<'_, DatabaseManager> = app.try_state().ok_or(
        OneClickLaunchError::ExecutionError("Unable to get DatabaseManager".to_string()),
    )?;

    let mut resources = launcher_resource::query_by_launcher_id(&db.pool, launcher_id).await?;

    tracing::debug!("启动编组原始资源列表: {resources:?}");

    // 必须从启动资源中排除自己,防止出现死循环
    let app_path = current_exe_path_str()?;
    resources.retain(|e| {
        // 检查路径是否指向当前应用程序
        !e.path.starts_with(&app_path)
    });

    if resources.is_empty() {
        tracing::debug!("资源列表为空");
        return Ok(());
    }

    let delay = get_launch_delay(&app).await?;

    launch_resources(&app, &resources, delay).await;

    EventDispatcher::<LauncherLaunched>::send_event(
        &app,
        LauncherLaunchedPayload {
            launcher_ids: vec![launcher_id],
        },
    )?;

    Ok(())
}

/// 打开路径
#[tauri::command]
pub async fn open_path(app: AppHandle, path: &str) -> Result<(), OneClickLaunchError> {
    open_using_default_program(&app, path)?;
    Ok(())
}

pub async fn launch_resources(app: &AppHandle, resources: &[LauncherResource], delay: Duration) {
    for (i, resource) in resources.iter().enumerate() {
        if let Err(e) = open_using_default_program(app, resource.path.as_str()) {
            info!(
                "启动资源失败,资源名称: {:?},资源路径: {:?},错误信息: {:?}",
                &resource.name, &resource.path, e
            );
        }
        // 最后一个资源之后不再等待
        if i < resources.len() - 1 {
            tokio::time::sleep(delay).await;
        }
    }
}

/// 使用系统默认的程序打开指定的文件或 URL。
///
/// # 参数
/// - `app`: 应用程序状态的引用，用于访问 Tauri 的应用句柄。
/// - `path`: 表示文件路径或 URL 的字符串切片。
///
/// # 返回值
/// - `Ok(())` 表示操作成功。
/// - `Err(OneClickLaunchError)` 表示操作失败。
pub fn open_using_default_program(app: &AppHandle, path: &str) -> Result<(), OneClickLaunchError> {
    match try_open_as_command(path) {
        Ok(true) => return Ok(()), // 已经作为程序执行
        Ok(false) => {
            // 不是程序，交给 opener
            open_path_with_opener(app, path)?;
        }
        Err(e) => {
            tracing::debug!("作为命令执行失败: {e:?}，尝试默认打开");
            open_path_with_opener(app, path)?;
        }
    }

    Ok(())
}

fn open_path_with_opener(app: &AppHandle, path: &str) -> Result<(), OneClickLaunchError> {
    app.opener()
        .open_path(path, None::<&str>)
        .map_err(|e| OneClickLaunchError::ExecutionError(e.to_string()))?;
    Ok(())
}

fn try_open_as_command(path: &str) -> Result<bool, OneClickLaunchError> {
    let parts = shlex::split(path)
        .ok_or_else(|| OneClickLaunchError::ExecutionError("无法解析路径".to_string()))?;

    if parts.is_empty() {
        return Ok(false);
    }

    let program = &parts[0];
    let args = &parts[1..];

    // 如果第一个部分是存在的文件（.exe/.bat/.sh 等），才当成命令执行
    if Path::new(program).exists() {
        Command::new(program).args(args).spawn()?;
        return Ok(true);
    }

    Ok(false)
}

#[tauri::command]
pub async fn create_handler_shortcut(
    launcher_id: i64,
    db: State<'_, DatabaseManager>,
) -> Result<String, OneClickLaunchError> {
    let launcher = launcher::find_by_id(&db.pool, launcher_id).await?;

    let app_path = current_exe_path_str()?;

    // 构建参数
    let args = Some(vec![format!("launch {}", launcher_id)]);

    window_api::create_shortcut(
        &app_path,
        &launcher.name,
        args,
        // None 表示保存到桌面
        None,
    )
    .map(|path| path.to_string_lossy().to_string())
}

fn current_exe_path_str() -> Result<String, OneClickLaunchError> {
    // 获取当前应用程序的绝对路径
    let exe_path = std::env::current_exe()?;

    // 转换为 Windows 可识别的普通路径
    let mut app_path = exe_path.to_string_lossy().to_string();
    if app_path.starts_with(r"\\?\") {
        app_path = app_path.trim_start_matches(r"\\?\").to_string();
    }
    Ok(app_path)
}

pub async fn get_launch_delay(app: &AppHandle) -> Result<Duration> {
    let db: State<'_, DatabaseManager> = app.try_state().ok_or(
        OneClickLaunchError::ExecutionError("Unable to get DatabaseManager".to_string()),
    )?;

    Ok(settings::read(&db.pool, RESOURCE_LAUNCH_INTERVAL)
        .await
        .unwrap_or_default()
        .map(|setting| setting.value)
        .map(|d| d.parse::<humantime::Duration>().unwrap().into())
        .unwrap_or_else(|| Duration::from_secs(0)))
}
